"""PPG处理器管理器

统一管理不同的PPG处理Pipeline，提供简单的接口来选择和使用不同的处理算法。
支持自定义Pipeline和NeuroKit2 Pipeline的动态切换。

主要功能：
1. Pipeline注册和管理
2. 根据名称选择Pipeline
3. 配置管理和验证
4. 统一的处理接口
5. 错误处理和日志记录

作者: PPG算法包开发团队
版本: 2.0.0
"""

import numpy as np
from typing import Dict, Any, Optional, Union, List
import logging
from pathlib import Path

try:
    from .data_types import PPGSignal, PPGResults
    from .base_pipeline import BasePipeline
    from ..config.pipeline_config import PipelineConfig
    from ..config.default_configs import get_config_by_name, DEFAULT_CUSTOM_CONFIG, DEFAULT_NEUROKIT2_CONFIG
    from ..pipelines.custom_pipeline import CustomPipeline
except ImportError:
    # 处理直接运行时的导入问题
    import sys
    sys.path.append(str(Path(__file__).parent.parent))
    from core.data_types import PPGSignal, PPGResults
    from core.base_pipeline import BasePipeline
    from config.pipeline_config import PipelineConfig
    from config.default_configs import get_config_by_name, DEFAULT_CUSTOM_CONFIG, DEFAULT_NEUROKIT2_CONFIG
    from pipelines.custom_pipeline import CustomPipeline

logger = logging.getLogger(__name__)

# 延迟导入NeuroKit2Pipeline以避免导入错误
try:
    from ..pipelines.neurokit2_pipeline import NeuroKit2Pipeline
    NEUROKIT2_AVAILABLE = True
except ImportError:
    NEUROKIT2_AVAILABLE = False
    logger.warning("NeuroKit2Pipeline不可用，请安装neurokit2包")


class PPGProcessorManager:
    """PPG处理器管理器
    
    提供统一的接口来管理和使用不同的PPG处理Pipeline
    """
    
    # 可用的Pipeline类型
    AVAILABLE_PIPELINES = {
        'custom': CustomPipeline,
        'neurokit2': NeuroKit2Pipeline if NEUROKIT2_AVAILABLE else None
    }
    
    def __init__(self):
        """初始化处理器管理器"""
        self._pipelines: Dict[str, BasePipeline] = {}
        self._configs: Dict[str, PipelineConfig] = {}
        logger.info("PPG处理器管理器初始化完成")
    
    def register_pipeline(self, name: str, pipeline: BasePipeline) -> None:
        """注册Pipeline实例
        
        参数:
            name: Pipeline名称
            pipeline: Pipeline实例
        """
        if not isinstance(pipeline, BasePipeline):
            raise ValueError(f"Pipeline必须继承自BasePipeline，得到: {type(pipeline)}")
        
        self._pipelines[name] = pipeline
        self._configs[name] = pipeline.config
        logger.info(f"已注册Pipeline: {name}")
    
    def create_pipeline(self, pipeline_type: str, config: Optional[Union[PipelineConfig, Dict, str]] = None) -> BasePipeline:
        """创建Pipeline实例
        
        参数:
            pipeline_type: Pipeline类型 ('custom' 或 'neurokit2')
            config: 配置对象、配置字典、配置名称或None（使用默认配置）
            
        返回:
            BasePipeline: 创建的Pipeline实例
            
        异常:
            ValueError: 当Pipeline类型不支持时
            ImportError: 当NeuroKit2不可用时
        """
        if pipeline_type not in self.AVAILABLE_PIPELINES:
            available = [k for k, v in self.AVAILABLE_PIPELINES.items() if v is not None]
            raise ValueError(f"不支持的Pipeline类型: {pipeline_type}，可用类型: {available}")
        
        pipeline_class = self.AVAILABLE_PIPELINES[pipeline_type]
        if pipeline_class is None:
            raise ImportError(f"Pipeline类型 '{pipeline_type}' 不可用，请检查依赖包是否已安装")
        
        # 处理配置
        if config is None:
            # 使用默认配置
            if pipeline_type == 'custom':
                config = PipelineConfig.from_dict(DEFAULT_CUSTOM_CONFIG)
            elif pipeline_type == 'neurokit2':
                config = PipelineConfig.from_dict(DEFAULT_NEUROKIT2_CONFIG)
        elif isinstance(config, str):
            # 从预定义配置名称创建
            config_dict = get_config_by_name(config)
            config = PipelineConfig.from_dict(config_dict)
        elif isinstance(config, dict):
            # 从字典创建
            config = PipelineConfig.from_dict(config)
        elif not isinstance(config, PipelineConfig):
            raise ValueError(f"配置必须是PipelineConfig、dict或str类型，得到: {type(config)}")
        
        # 创建Pipeline实例
        pipeline = pipeline_class(config)
        logger.info(f"创建Pipeline: {pipeline_type}, 名称: {pipeline.name}")
        
        return pipeline
    
    def get_or_create_pipeline(self, pipeline_name: str, 
                              pipeline_type: Optional[str] = None,
                              config: Optional[Union[PipelineConfig, Dict, str]] = None) -> BasePipeline:
        """获取或创建Pipeline实例
        
        参数:
            pipeline_name: Pipeline名称
            pipeline_type: Pipeline类型（如果需要创建）
            config: 配置（如果需要创建）
            
        返回:
            BasePipeline: Pipeline实例
        """
        # 如果已存在，直接返回
        if pipeline_name in self._pipelines:
            return self._pipelines[pipeline_name]
        
        # 如果不存在，尝试创建
        if pipeline_type is None:
            # 尝试从名称推断类型
            if 'neurokit' in pipeline_name.lower() or 'nk' in pipeline_name.lower():
                pipeline_type = 'neurokit2'
            else:
                pipeline_type = 'custom'
        
        pipeline = self.create_pipeline(pipeline_type, config)
        self.register_pipeline(pipeline_name, pipeline)
        
        return pipeline
    
    def process_signal(self, signal_data: Union[np.ndarray, Dict, PPGSignal],
                      pipeline_name: str = "default_custom",
                      pipeline_type: Optional[str] = None,
                      config: Optional[Union[PipelineConfig, Dict, str]] = None,
                      sampling_rate: Optional[float] = None) -> PPGResults:
        """处理PPG信号
        
        参数:
            signal_data: 信号数据（numpy数组、字典或PPGSignal对象）
            pipeline_name: Pipeline名称
            pipeline_type: Pipeline类型（如果需要创建新Pipeline）
            config: 配置（如果需要创建新Pipeline）
            sampling_rate: 采样率（当signal_data为numpy数组时需要）
            
        返回:
            PPGResults: 处理结果
            
        异常:
            ValueError: 当输入参数无效时
        """
        # 转换输入为PPGSignal对象
        ppg_signal = self._convert_to_ppg_signal(signal_data, sampling_rate)
        
        # 获取或创建Pipeline
        pipeline = self.get_or_create_pipeline(pipeline_name, pipeline_type, config)
        
        # 处理信号
        logger.info(f"使用Pipeline '{pipeline_name}' 处理PPG信号")
        results = pipeline.process(ppg_signal)
        
        return results
    
    def batch_process(self, signals: List[Union[np.ndarray, Dict, PPGSignal]],
                     pipeline_name: str = "default_custom",
                     pipeline_type: Optional[str] = None,
                     config: Optional[Union[PipelineConfig, Dict, str]] = None,
                     sampling_rate: Optional[float] = None) -> List[PPGResults]:
        """批量处理PPG信号
        
        参数:
            signals: 信号列表
            pipeline_name: Pipeline名称
            pipeline_type: Pipeline类型
            config: 配置
            sampling_rate: 采样率
            
        返回:
            List[PPGResults]: 处理结果列表
        """
        logger.info(f"开始批量处理 {len(signals)} 个PPG信号")
        
        results = []
        for i, signal_data in enumerate(signals):
            try:
                result = self.process_signal(
                    signal_data, pipeline_name, pipeline_type, config, sampling_rate
                )
                results.append(result)
                logger.debug(f"信号 {i+1}/{len(signals)} 处理完成")
            except Exception as e:
                logger.error(f"信号 {i+1} 处理失败: {e}")
                # 创建空结果
                ppg_signal = self._convert_to_ppg_signal(signal_data, sampling_rate)
                empty_result = PPGResults(
                    original_signal=ppg_signal.data,
                    processed_signal=ppg_signal.data,
                    peaks=np.array([]),
                    heart_rates=None,
                    mean_heart_rate=0.0,
                    hrv_results=None,
                    quality_results=None,
                    processing_info={"error": str(e)},
                    pipeline_name=pipeline_name
                )
                results.append(empty_result)
        
        logger.info(f"批量处理完成，成功: {len([r for r in results if 'error' not in r.processing_info])}")
        return results
    
    def list_pipelines(self) -> Dict[str, Dict[str, Any]]:
        """列出所有已注册的Pipeline
        
        返回:
            Dict: Pipeline信息字典
        """
        pipeline_info = {}
        for name, pipeline in self._pipelines.items():
            pipeline_info[name] = {
                'type': pipeline.__class__.__name__,
                'config_name': pipeline.config.name,
                'sampling_rate': pipeline.sampling_rate,
                'created_at': getattr(pipeline, 'created_at', 'unknown')
            }
        return pipeline_info
    
    def list_available_types(self) -> List[str]:
        """列出可用的Pipeline类型
        
        返回:
            List[str]: 可用类型列表
        """
        return [k for k, v in self.AVAILABLE_PIPELINES.items() if v is not None]
    
    def remove_pipeline(self, pipeline_name: str) -> bool:
        """移除Pipeline实例
        
        参数:
            pipeline_name: Pipeline名称
            
        返回:
            bool: 是否成功移除
        """
        if pipeline_name in self._pipelines:
            del self._pipelines[pipeline_name]
            if pipeline_name in self._configs:
                del self._configs[pipeline_name]
            logger.info(f"已移除Pipeline: {pipeline_name}")
            return True
        return False
    
    def clear_pipelines(self) -> None:
        """清除所有Pipeline实例"""
        self._pipelines.clear()
        self._configs.clear()
        logger.info("已清除所有Pipeline实例")
    
    def _convert_to_ppg_signal(self, signal_data: Union[np.ndarray, Dict, PPGSignal],
                              sampling_rate: Optional[float] = None) -> PPGSignal:
        """转换输入为PPGSignal对象
        
        参数:
            signal_data: 输入信号数据
            sampling_rate: 采样率
            
        返回:
            PPGSignal: PPG信号对象
            
        异常:
            ValueError: 当输入格式无效时
        """
        if isinstance(signal_data, PPGSignal):
            return signal_data
        elif isinstance(signal_data, np.ndarray):
            if sampling_rate is None:
                raise ValueError("当输入为numpy数组时，必须提供sampling_rate参数")
            return PPGSignal(data=signal_data, sampling_rate=sampling_rate)
        elif isinstance(signal_data, dict):
            # 从字典创建PPGSignal
            if 'data' not in signal_data:
                raise ValueError("字典输入必须包含'data'字段")
            
            data = np.array(signal_data['data'])
            sr = signal_data.get('sampling_rate', sampling_rate)
            
            if sr is None:
                raise ValueError("必须在字典中提供'sampling_rate'或作为参数提供")
            
            metadata = {k: v for k, v in signal_data.items() 
                       if k not in ['data', 'sampling_rate']}
            
            return PPGSignal(data=data, sampling_rate=sr, metadata=metadata)
        else:
            raise ValueError(f"不支持的信号数据类型: {type(signal_data)}")


# 全局处理器管理器实例
_global_manager = None


def get_global_manager() -> PPGProcessorManager:
    """获取全局处理器管理器实例
    
    返回:
        PPGProcessorManager: 全局管理器实例
    """
    global _global_manager
    if _global_manager is None:
        _global_manager = PPGProcessorManager()
    return _global_manager


def process_ppg_signal(signal_data: Union[np.ndarray, Dict, PPGSignal],
                      pipeline_name: str = "default_custom",
                      pipeline_type: Optional[str] = None,
                      config: Optional[Union[PipelineConfig, Dict, str]] = None,
                      sampling_rate: Optional[float] = None) -> PPGResults:
    """便捷函数：处理PPG信号
    
    参数:
        signal_data: 信号数据
        pipeline_name: Pipeline名称
        pipeline_type: Pipeline类型
        config: 配置
        sampling_rate: 采样率
        
    返回:
        PPGResults: 处理结果
    """
    manager = get_global_manager()
    return manager.process_signal(signal_data, pipeline_name, pipeline_type, config, sampling_rate)


def create_ppg_pipeline(pipeline_type: str, 
                       config: Optional[Union[PipelineConfig, Dict, str]] = None) -> BasePipeline:
    """便捷函数：创建PPG Pipeline
    
    参数:
        pipeline_type: Pipeline类型
        config: 配置
        
    返回:
        BasePipeline: Pipeline实例
    """
    manager = get_global_manager()
    return manager.create_pipeline(pipeline_type, config)